#include "Cilindro.h"

#include <cmath>


Cilindro::Cilindro() : 
	m_cantLados(16), 
	m_cantVertices_tapa_base( this->m_cantLados +1 ),
	m_cantVertices_lateral( 2 * this->m_cantLados )
{
	/** NOTA:
	 *	Cuando mayor es la cantidad de lados mas "redondo" es el cilindro.
	 *	Las tapas se construyen mediante "GL_TRIANGLE_FAN" y por esto se necesita
	 *	el vertice del centro
	 **/

	// Los vertices de la base, la tapa y los laterales
	this->m_tapa = new glm::vec3[ this->m_cantVertices_tapa_base ];
	this->m_base = new glm::vec3[ this->m_cantVertices_tapa_base ];
	this->m_lateral = new glm::vec3[ this->m_cantVertices_lateral ];

	// Las normales de los elementos
	this->m_normales_tapa = new glm::vec3[ this->m_cantVertices_tapa_base ];
	this->m_normales_base = new glm::vec3[ this->m_cantVertices_tapa_base ];
	this->m_normales_lateral = new glm::vec3[ this->m_cantVertices_lateral ];

	// Los indices, se suma uno porque hay que cerrar la figura
	this->m_indexBuff_tapa = new GLuint[ this->m_cantVertices_tapa_base +1 ];
	this->m_indexBuff_base = new GLuint[ this->m_cantVertices_tapa_base +1 ];
	this->m_indexBuff_lateral = new GLuint[ this->m_cantVertices_lateral +2 ];

	// Para usar el "GL_TRIANGLE_FAN" se necesita el punto central
	this->m_tapa[0] = glm::vec3(0.0f, 0.0f, 1.0f);
	this->m_base[0] = glm::vec3(0.0f, 0.0f, -1.0f);
	this->m_normales_tapa[0] = glm::vec3(0.0f, 0.0f, 1.0f);
	this->m_normales_base[0] = glm::vec3(0.0f, 0.0f, -1.0f);
	this->m_indexBuff_tapa[0] = 0;
	this->m_indexBuff_base[0] = 0;

	// Se divide el circulo en "m_cantLados" partes y se calculan los vertices
	const float angulo = 2.0f * 3.1416f / this->m_cantLados;
	unsigned int posLateral = 0;
	for (unsigned int i = 1; i < this->m_cantVertices_tapa_base; ++i){
		// Las posiciones de los puntos
		float x = std::sin(i * angulo);
		float y = std::cos(i * angulo);

		// Los vertices, normales e indices de la tapa y la base
		this->m_tapa[i] = glm::vec3(x, y, 1.0f);
		this->m_base[i] = glm::vec3(x, y, -1.0f);
		this->m_normales_tapa[i] = glm::vec3(0.0f, 0.0f, 1.0f);
		this->m_normales_base[i] = glm::vec3(0.0f, 0.0f, -1.0f);
		this->m_indexBuff_tapa[i] = i;
		this->m_indexBuff_base[i] = i;

		// Los laterales, las normales y sus indices
		this->m_lateral[posLateral] = glm::vec3(x, y, 1.0f);
		this->m_normales_lateral[posLateral] = glm::vec3(x, y, 0.0f);
		this->m_indexBuff_lateral[posLateral] = posLateral;
		++posLateral;

		this->m_lateral[posLateral] = glm::vec3(x, y, -1.0f);
		this->m_normales_lateral[posLateral] = glm::vec3(x, y, 0.0f);
		this->m_indexBuff_lateral[posLateral] = posLateral;
		++posLateral;
	}

	// El indice que cierra el ciclo de la tapa, la base y los laterales
	this->m_indexBuff_tapa[this->m_cantVertices_tapa_base] = 1;
	this->m_indexBuff_base[this->m_cantVertices_tapa_base] = 1;
	this->m_indexBuff_lateral[this->m_cantVertices_lateral] = 0;
	this->m_indexBuff_lateral[this->m_cantVertices_lateral +1] = 1;
}


Cilindro::~Cilindro(){
	delete[] this->m_tapa;
	delete[] this->m_base;
	delete[] this->m_indexBuff_tapa;
	delete[] this->m_indexBuff_base;
	delete[] this->m_normales_tapa;
	delete[] this->m_normales_base;

	delete[] this->m_lateral;
	delete[] this->m_indexBuff_lateral;
	delete[] this->m_normales_lateral;
}


void Cilindro::renderizar(const glm::mat4 &matVista, const glm::mat4 &matProyeccion, const glm::mat4 &matModelo) const {
	// Carga el shader
	Renderizable::cargarShaderProgram();

	// Empieza a dibujar todo
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);

	// Los datos de iluminacion y de tranformaciones
	Renderizable::cargarDatosIluminacion(matModelo);
	Renderizable::cargarMatricesTransformacion(matVista, matProyeccion, matModelo);

	// La tapa del cilindro
	glVertexPointer(3, GL_FLOAT, 0, this->m_tapa);
	glNormalPointer(GL_FLOAT, 0, this->m_normales_tapa);
	glDrawElements(GL_TRIANGLE_FAN, this->m_cantVertices_tapa_base +1, GL_UNSIGNED_INT, this->m_indexBuff_tapa);

	// La base del cilindro
	glVertexPointer(3, GL_FLOAT, 0, this->m_base);
	glNormalPointer(GL_FLOAT, 0, this->m_normales_base);
	glDrawElements(GL_TRIANGLE_FAN, this->m_cantVertices_tapa_base +1, GL_UNSIGNED_INT, this->m_indexBuff_base);

	// Los lados del cilindro
	glVertexPointer(3, GL_FLOAT, 0, this->m_lateral);
	glNormalPointer(GL_FLOAT, 0, this->m_normales_lateral);
	glDrawElements(GL_TRIANGLE_STRIP, this->m_cantVertices_lateral +2, GL_UNSIGNED_INT, this->m_indexBuff_lateral);

	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
}


